#include "window.h"
#include <QXmlStreamReader>
#include "script.h"
#include "context.h"
#include "windowmanager.h"

static RegisterElementType r("Window", Window());

Window::Window()
{
    setVisiable(false);     // Window default is disvisiable.

    connect(this, SIGNAL(keyEvent(int,int)),
            this, SLOT(onKeyEvent(int,int)));
    connect(this, SIGNAL(moveEvent(MoveDirection)),
            this, SLOT(onMoveEvent(MoveDirection)));
}

bool Window::copyFrom(const Element *e)
{
    const Window *w = qobject_cast<const Window*>(e);
    if(!w) return NULL;

    elements_ = w->elements_;
    anymous_elements_ = w->anymous_elements_;
    size_hint_ = w->size_hint_;
    pre_load_fun_ = w->pre_load_fun_;
    post_load_fun_ = w->post_load_fun_;

    return Element::copyFrom(this);
}

Element *Window::clone() const
{
    Window *w = new Window();
    if(!w->copyFrom(this)){
        delete w;
        w = NULL;
    }

    return w;
}

QRect Window::sizeHint() const
{
    return size_hint_;
}

bool Window::addElement(Element *e, bool setParent)
{
    if(!e) return false;
    bool added = true;
    if(e->name().isEmpty()){
        anymous_elements_.insert(e, e);
    }else{
        if(elements_.contains(e->name())){
            added = false;
        }else{
            elements_.insert(e->name(), e);
        }
    }
    if(added && setParent){
        e->setParent(this);
    }
    if(added){
       size_hint_.setX(qMin(size_hint_.x(), e->x()));
       size_hint_.setY(qMin(size_hint_.y(), e->y()));

       size_hint_.setWidth(qMax(size_hint_.width(), e->x()-size_hint_.x()+e->width()));
       size_hint_.setHeight(qMax(size_hint_.height(), e->y()-size_hint_.y()+e->height()));

       auto keys = e->bindingKeys();
       for(auto key:keys){
           key_elements_[key].append(e);
       }
       if(e->needFocus()){
           auto iter=need_focus_elements_.begin();
           for(;
               iter!=need_focus_elements_.end();
               ++iter){
               if((*iter)->x()>e->x()){
                   break;
               }
           }
           if(!need_focus_elements_.isEmpty()){
               need_focus_elements_.first()->setFocus(false);
           }
           need_focus_elements_.insert(iter, e);
           need_focus_elements_.first()->setFocus(true);
       }
    }
    return added;
}

Element *Window::element(const QString &name) const
{
    return elements_.value(name);
}

void Window::switchWindowTo(quint32 id)
{
    WindowManager *wm = windowManager();
    if(wm) wm->switchWindowTo(id);
}

void Window::paintEvent()
{
    for(QObject *o:children()){
        Element *e = qobject_cast<Element*>(o);
        if(!e || e->isHide()) continue;

        e->update();
    }
}

void Window::hideEvent()
{
    clearRect(rect());
}

void Window::setExternData(const QString &name, const QVariant &data)
{
    extern_data_.insert(name, data);
}

QVariant Window::externData(const QString &name)
{
    return extern_data_.value(name);
}

void Window::addPreLoad(const Window::Hook &h)
{
    pre_load_fun_.append(h);
}

void Window::preLoad(WindowManager *wm)
{
    for(auto fun:pre_load_fun_){
        fun();
    }

    connect(wm, &WindowManager::keyEvent, this, &Window::onKeyEvent);
    connect(wm, &WindowManager::moveEvent, this, &Window::onMoveEvent);
}

void Window::addPostLoad(const Window::Hook &h)
{
    post_load_fun_.append(h);
}

void Window::postLoad(WindowManager *wm)
{
    for(auto fun:post_load_fun_){
        fun();
    }

    wm->disconnect(wm, &WindowManager::keyEvent, this, 0);
    wm->disconnect(wm, &WindowManager::moveEvent, this, 0);
}

QList<int> Window::bindingKeysInWindow() const
{
    return key_elements_.keys();
}

bool Window::isBindKeyInWindow(int key) const
{
    return key_elements_.contains(key);
}

void Window::onKeyEvent(int key, int value)
{
    static QVector<Element*> empty;
    QVector<Element*> v = key_elements_.value(key, empty);
    for(Element *e:v){
        if(!e->needFocus()||e->hasFocus())
            e->keyboardEvent(key, value);
    }
}

void Window::onMoveEvent(MoveDirection direction)
{
    if(need_focus_elements_.isEmpty()) return;

    int size = need_focus_elements_.size();
    int current_focus = -1;
    for(int i=0; i<size; ++i){
        if(need_focus_elements_[i]->hasFocus()){
            current_focus = i;
            break;
        }
    }
    Q_ASSERT(current_focus!=-1);
    need_focus_elements_[current_focus]->setFocus(false);

    current_focus += direction==Left?-1:1;
    current_focus %= size;

    need_focus_elements_[current_focus]->setFocus(true);

    update();
}
